package k8.buffer;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

public abstract class Buffer {
    
    public ByteBuffer buffer;
    
    protected int elements;
    protected int initialcapacity;
    protected boolean flush;
    
    // Cache of reusable discarded slices
	private List<Slice> cache;
    
    /**
     * Creates an instance of Buffer.
     * 
     * @param capacity
     *          Initial capacity (number of elements)
     */
    public Buffer(int initialcapacity) {
    	buffer = null;
    	elements = 0;
    	this.initialcapacity = initialcapacity;
    	flush = false;
    	cache = new ArrayList<Slice>();
    }
    
    /**
     * Gets a Slice of this buffer.
     * 
     * If there is insufficent capacity, the buffer is grown by a factor of 25%.
     * Markers are reset and null is returned. Subsequent calls to getSlice() will
     * return Slices starting from the beginning of the buffer again.
     */
    protected Slice getSlice(int bytes) {
    	// Use discarded slices, if available
        if ( cache.size() > 0 )	return cache.remove(0);
        
        // Is this the first allocation?
        if ( buffer == null ) {
            buffer = ByteBuffer.allocateDirect(initialcapacity).order(ByteOrder.nativeOrder());
        	buffer.flip();
        }
        
        // Does the buffers size need to be increased?
    	if ( buffer.limit() + bytes > buffer.capacity() ) {
    		buffer = ByteBuffer.allocateDirect((int)Math.ceil((buffer.capacity() + bytes) * 1.25)).order(ByteOrder.nativeOrder());
    		buffer.flip();
    		elements = 0;
    		return null;
    	}
    	
    	// Create a Slice of limit + bytes, update elements count and return Slice
    	buffer.position(buffer.limit());
        buffer.limit(buffer.position() + bytes);
   		elements = buffer.limit() / 4;
   		return new Slice(this, buffer.slice().order(ByteOrder.nativeOrder()));
    }
    
    /**
     * Disgards the specified Slice.
     * 
     * The disgarded Slice is chached for reuse by future getSlice() requests.
     */
    public void disgard(Slice slice) {
    	if ( slice == null ) return;
    	slice.clear();
    	cache.add(slice);
    }
    
    /** Flags the buffer for a flush before its next used */
    public void flush() {
    	flush = true;
    }
    
    /** Gets the number of allocated elements */
    public int getElements() {
    	return elements;
    }
    
    /** Destroys the buffer */
    public abstract void destroy();
    
}
